#include <lua5.4/lua.hpp>
#include <string>
#include "work/luaManager.h"
#include "work/exposeFunc.h"
#include "work/workglobal.h"

LuaManager::LuaManager(/* args */)
{
    m_lua = nullptr;
    m_funcMap.clear();
}

LuaManager::~LuaManager()
{
    if (m_lua)
        lua_close(m_lua);
}

/**
 * 初始化lua状态机的函数
 * @return 返回初始化结果
 */
bool LuaManager::Initialize()
{
    // 1.创建lua状态机
    m_lua = luaL_newstate();
    if (!m_lua)
    {
        Log_Error(0, "创建lua状态机失败");
        return false;
    }

    // 2.加载lua标准库
    luaL_openlibs(m_lua);

    // 3.加载系统需要暴露的函数(一定要在加载脚本入口前!!!)
    for (auto it = m_funcMap.begin(); it != m_funcMap.end(); ++it)
    {
        if (!Register2Lua(it->first, it->second))
        {
            Log_Error(0, "注册函数%s失败", it->first.c_str());
            return false; // 有一个注册失败就返回
        }
    }

    // 4.加载lua脚本层入口函数
    if (luaL_dofile(m_lua, "../main.lua") != LUA_OK)
    {
        std::string err = lua_tostring(m_lua, -1);
        Log_Error(0, "lua脚本层入口函数错误信息:%s", err.c_str());
        lua_pop(m_lua, 1); // 弹出错误信息,避免栈堆积
        lua_close(m_lua);
        return false;
    }
    return true;
}

/**
 * 往注册容器中添加待注册函数
 * @param name lua中调用的函数名称
 * @param callback 具体绑定的C++函数
 */
void LuaManager::PutNewExposeFunc(std::string name, luaCallback callback)
{
    m_funcMap.emplace(name, callback);
}

/**
 * 自行封装的向lua注册C函数
 * @return 返回成功与否
 * @param name lua中调用的函数名称
 * @param callback 具体绑定的C++函数
 */
bool LuaManager::Register2Lua(std::string name, luaCallback callback)
{
    lua_pushcfunction(m_lua, callback);
    lua_setglobal(m_lua, name.c_str());
    return true;
}

/**
 * 由C++调用lua函数
 */
void LuaManager::DoLuaFunc(const char *name, const char *data, size_t size)
{
    // 1.检查输入有效性
    if (!data || size == 0 || !m_lua)
        return;

    // 2.根据名称掉用处理函数
    lua_getglobal(m_lua, name);
    if (!lua_isfunction(m_lua, -1))
    {
        Log_Error(0, "DoLuaFunc函数尝试调用lua函数%s 失败,找不到此函数", name);
        lua_pop(m_lua, 1); // 清理栈
        return;
    }

    // 3.推送参数（二进制）
    lua_pushlstring(m_lua, data, size);

    // 4.调用函数
    int ret = lua_pcall(m_lua, 1, 0, 0);
    if (ret != LUA_OK)
    {
        Log_Error(0, "DoLuaFunc函数尝试调用lua函数%s 失败，参数%s", name, data);
        lua_pop(m_lua, 1); // 清理错误信息
    }
}